// package person "人员"的通用表示
package person

import (
	"context"

	"shop-manager/internal/do"

	"github.com/samber/lo"
	"gorm.io/gorm"
)

const (
	MaxPageSize     = 100
	DefaultPageSize = 10
)

//go:generate newc
type PersonRepo struct {
	db *gorm.DB
}

// 保持两个参数, 统一处理
func (s *PersonRepo) Create(ctx context.Context, d *do.Person) (id int, err error) {
	err = s.db.WithContext(ctx).Create(d).Error
	return d.ID, err
}

// 只限部分字段可修改, 其余字段不允许修改
func (s *PersonRepo) Update(ctx context.Context, d *do.Person) (err error) {
	origin := &do.Person{}
	if err := s.db.WithContext(ctx).First(origin, d.ID).Error; err != nil {
		return err
	}

	// 只允许修改这几个字段
	origin.Name = d.Name
	origin.Note = d.Note
	origin.Type = d.Type

	return s.db.WithContext(ctx).Save(origin).Error
}

type ListParam struct {
	Page     int
	PageSize int

	IDs    []int  // 指定ID列表
	Search string // 模糊搜索
	// 排序, 格式为 field [ASC/DESC], ...
	Sorts string

	PersonType do.PersonType
}

func (s *PersonRepo) List(ctx context.Context, param ListParam) (listData []*do.Person, total int64, err error) {
	if param.Page <= 0 {
		param.Page = 1
	}
	if param.PageSize == 0 { // -1 means no limit
		param.PageSize = DefaultPageSize
	}
	if param.PageSize > MaxPageSize {
		param.PageSize = MaxPageSize
	}

	err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if len(param.IDs) > 0 {
			tx = tx.Where("id IN ?", param.IDs)
		}
		if param.Search != "" {
			tx = tx.Where("name LIKE ?", "%"+param.Search+"%")
		}
		if param.Sorts != "" {
			tx = tx.Order(param.Sorts)
		}
		if param.PersonType != 0 {
			tx = tx.Where("type = ?", param.PersonType)
		}

		err = tx.Model(&do.Person{}).Find(&listData).Count(&total).Error
		if err != nil {
			return err
		}

		offset := (param.Page - 1) * param.PageSize
		err = tx.Model(&do.Person{}).
			Offset(offset).
			Limit(param.PageSize).
			Find(&listData).Error
		if err != nil {
			return err
		}

		return err
	})
	return listData, total, err
}

func (s *PersonRepo) Delete(ctx context.Context, id int) error {
	return s.db.WithContext(ctx).Delete(&do.Person{}, id).Error
}

func (s *PersonRepo) GetPersonMap(ctx context.Context, param ListParam) (m map[int]*do.Person, err error) {
	listData, _, err := s.List(ctx, param)
	if err != nil {
		return
	}
	return lo.Associate(listData, func(item *do.Person) (int, *do.Person) {
		return item.ID, item
	}), nil
}
